﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace WinToastr
{
    public static class Toastr
    {
        private static Timer animationTimer = new Timer();
        private static Timer delayTimer = new Timer();
        private static float alphaval = 0;
        private static float incr = 0.1F;

        private static Control ParentControl { get; set; }
        private static ToastrControl PopupControl { get; set; }
        private static ToastrType ToastType { get; set; }
        private static String MessageText { get; set; }
        private static Boolean IsVisible { get; set; }


        /// <summary>
        /// Gets or sets the timer delay (in milliseconds). Default 5000 ms.
        /// </summary>
        /// <value>
        /// The delay.
        /// </value>
        public static Int32 Delay { get; set; }

        /// <summary>
        /// Gets or sets the message font.
        /// </summary>
        /// <value>
        /// The message font.
        /// </value>
        public static Font MessageFont { get; set; }

        /// <summary>
        /// Initializes the <see cref="Toastr"/> class.
        /// </summary>
        static Toastr()
        {
            ToastType = ToastrType.Info;
            MessageText = String.Empty;
            IsVisible = false;
            Delay = 5000;
        }

        /// <summary>
        /// Shows the Toastr.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="toastType">Type of the toast.</param>
        /// <param name="messageText">The message text.</param>
        public static void Show(Control parent, ToastrType toastType, String messageText)
        {
            if (MessageFont == null)
                MessageFont = parent.Font;

            Show(parent, toastType, MessageFont, messageText);
        }

        /// <summary>
        /// Shows the Toastr.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="toastType">Type of the toast.</param>
        /// <param name="font">The font.</param>
        /// <param name="messageText">The message text.</param>
        public static void Show(Control parent, ToastrType toastType, Font font, String messageText)
        {
            if(parent == null)
                return;

            // Clear resources.
            Dispose();

            // Setup notification window.
            IsVisible = true;
            ParentControl = parent;
            MessageText = messageText;
            ToastType = toastType;
            MessageFont = font;
            PopupControl = new ToastrControl();
            PopupControl.IsTransparent = true;
            PopupControl.Paint += OnPaint;
            PopupControl.Click += OnClick;
            ParentControl.Resize += OnResize;
            
            // Measure message.
            Measure();

            //Set up animation
            animationTimer = new Timer();
            animationTimer.Interval = 15;
            animationTimer.Enabled = true;
            animationTimer.Tick += AnimationTimer_Tick;

            delayTimer = new Timer();
            delayTimer.Interval = Delay;
            delayTimer.Tick += DelayTimer_Tick;
        }

        /// <summary>
        /// Close the Toast on click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        private static void OnClick(Object sender, EventArgs e)
        {
            Dispose();
        }

        /// <summary>
        /// Resize and reposition when parent is resized.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        private static void OnResize(Object sender, EventArgs e)
        {
            Measure();
        }

        /// <summary>
        /// Calculate size and position of the control.
        /// </summary>
        private static void Measure()
        {
            if (!IsVisible)
                return;

            SizeF textSize = PopupControl.CreateGraphics().MeasureString(MessageText, MessageFont);
            PopupControl.Height = 25 + (Int32)textSize.Height;
            PopupControl.Width = 50 + (Int32)textSize.Width;
            if (textSize.Width > ParentControl.Width - 100)
            {
                PopupControl.Width = ParentControl.Width - 100;
                Int32 hf = (Int32)textSize.Width / (ParentControl.Width - 100);
                PopupControl.Height += (Int32)textSize.Height * hf;
            }

            // Position control in parent
            PopupControl.Left = (ParentControl.Width - PopupControl.Width) / 2;
            PopupControl.Top = (ParentControl.Height - PopupControl.Height);
            ParentControl.Controls.Add(PopupControl);
            PopupControl.BringToFront();
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        private static void Dispose()
        {
            if (IsVisible)
            {
                animationTimer.Dispose();
                delayTimer.Dispose();
                PopupControl.Dispose();
                incr = 0.1F;
                alphaval = 0;
                ParentControl.Resize -= OnResize;
            }
        }
                
        /// <summary>
        /// Handles the delay.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private static void DelayTimer_Tick(Object sender, EventArgs e)
        {
            incr = -0.1F;
            animationTimer.Enabled = true;
            delayTimer.Enabled = false;
        }

        /// <summary>
        /// Handles the window animation.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private static void AnimationTimer_Tick(Object sender, EventArgs e)
        {
            if (incr > 0 && alphaval < 1)
            {
                if (alphaval + incr <= 1)
                {
                    alphaval += incr;
                    PopupControl.Refresh();
                }
                else
                {
                    alphaval = 1;
                    PopupControl.Refresh();
                    animationTimer.Enabled = false;
                    delayTimer.Enabled = true;
                }
            }
            else if (alphaval > 0)
            {
                if (alphaval + incr >= 0)
                {
                    alphaval += incr;
                    PopupControl.Refresh();
                }
                else
                {
                    alphaval = 0;
                    animationTimer.Enabled = false;
                    animationTimer.Dispose();
                    delayTimer.Dispose();
                    PopupControl.Dispose();
                    incr = 0.1F;
                    IsVisible = false;
                }
            }
        }

        /// <summary>
        /// Handles the paint event of the display control.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="pe">The <see cref="PaintEventArgs" /> instance containing the event data.</param>
        private static void OnPaint(Object sender, PaintEventArgs pe)
        {
            // This BITMAP object will hold the appearance of the notification dialog.
            Bitmap img = new Bitmap(PopupControl.Width, PopupControl.Height);
            Graphics e = Graphics.FromImage(img);
            e.SmoothingMode = SmoothingMode.AntiAlias;

            // Drawing Tools
            Brush brush = new SolidBrush(ToastrColor());
            Pen pen = new Pen(brush, 6);
            GraphicsPath gp = new GraphicsPath();
            pen.LineJoin = LineJoin.Round;

            // Draw borders Outmost
            gp.AddRectangle(new Rectangle(3, 3, PopupControl.Width - 10, PopupControl.Height - 10));
            e.DrawPath(pen, gp);

            // level 3, A bit solid
            gp.Reset();
            gp.AddRectangle(new Rectangle(5, 5, PopupControl.Width - 14, PopupControl.Height - 14));
            e.DrawPath(pen, gp);
            gp.Reset();
            gp.AddRectangle(new Rectangle(7, 7, PopupControl.Width - 18, PopupControl.Height - 18));
            e.DrawPath(pen, gp);

            // level 2, a bit more solid
            gp.Reset();
            gp.AddRectangle(new Rectangle(7, 7, PopupControl.Width - 18, PopupControl.Height - 18));
            e.DrawPath(pen, gp);

            // level 1, more solidness
            gp.Reset();
            gp.AddRectangle(new Rectangle(9, 9, PopupControl.Width - 22, PopupControl.Height - 22));
            e.DrawPath(pen, gp);

            // Draw Content Rectangle.
            gp.Reset();
            gp.AddRectangle(new Rectangle(8, 8, PopupControl.Width - 20, PopupControl.Height - 20));
            e.DrawPath(pen, gp);
            e.FillRectangle(brush, new Rectangle(9, 9, PopupControl.Width - 21, PopupControl.Height - 21));

            // Set COLORMATRIX (RGBAw).
            // Matrix [3,3] will be the Alpha. Alpha is in float, 0(transparent) - 1(opaque).
            ColorMatrix colorMatrix = new ColorMatrix();
            colorMatrix.Matrix33 = alphaval;
            ImageAttributes imgAttributes = new ImageAttributes();
            imgAttributes.SetColorMatrix(colorMatrix);

            // Draw the notification message..
            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Center;
            e.DrawString(MessageText, MessageFont, new SolidBrush(Color.FromArgb(247, 247, 247)), new Rectangle(9, 9, PopupControl.Width - 21, PopupControl.Height - 21), sf);

            // Now, draw the content on the control.
            pe.Graphics.DrawImage(img, new Rectangle(0, 0, PopupControl.Width, PopupControl.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttributes);

            // Free the memory.
            colorMatrix = null;
            sf.Dispose();
            imgAttributes.Dispose();
            e.Dispose();
            img.Dispose();
            brush.Dispose();
            pen.Dispose();
            gp.Dispose();
        }

        /// <summary>
        /// Return color based on Toastr Type.
        /// </summary>
        /// <returns></returns>
        private static Color ToastrColor()
        {
            if (ToastType == ToastrType.Success)
                return Color.FromArgb(81, 163, 81);
            else if (ToastType == ToastrType.Error)
                return Color.FromArgb(189, 54, 47);
            else
                return Color.FromArgb(47, 150, 180);
        }
    }
}
